package naga.x.common;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import naga.x.game.time.TimeUpdater;

public class SimpleUpdater implements Updater {

	private List<Updatable> updatables = new ArrayList<Updatable>();
	private List<Async> asyncUpdatables = new ArrayList<Async>();
	private ExecutorService asyncExecutor = new ThreadPoolExecutor(4, 4, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
		@Override
		public Thread newThread(Runnable r) {
			Thread t = new Thread(r, "Updater-Async");
			t.setDaemon(true);
			return t;
		}
	});
	
	private static final Logger LOG = LoggerFactory.getLogger(SimpleUpdater.class);
	
	static class Async implements Runnable {
		boolean done;
		Updatable ref;
		Async(Updatable ref) {
			this.done = true;
			this.ref = ref;
		}
		@Override
		public void run() {
			try {
				run0();
			} catch (Exception e) {
				LOG.error("[ASYNC CYCLE ERROR]CLASS[" + ref.getClass().getName() + "]", e);
			}
		}
		private void run0() {
			long l = TimeUpdater.getInstance().utc();
			synchronized (this) {
				done = false;
			}
			ref.update();
			synchronized (this) {
				done = true;
			}
			l = TimeUpdater.getInstance().utc() - l;
			if (l > 1000) {
				LOG.info("[ASYNC CYCLE TOO LONG]" + l + "[" + ref.getClass().getName() + "]");
			}
		}
		synchronized boolean isDone() {
			return done;
		}
	}
	
	@Override
	public boolean update() {
		List<Updatable> list = updatables;
		int size = list.size();
		for (int i = 0; i < size; i++) {
			long l = TimeUpdater.getInstance().utc();
			Updatable u = list.get(i);
			try {
				u.update();
			} catch (Exception e) {
				LOG.error("[MAIN CYCLE ERROR]CLASS[" + u.getClass().getName() + "]", e);
			}
			l = TimeUpdater.getInstance().utc() - l;
			if (l > 100) {
				LOG.info("[MAIN CYCLE TOO LONG]" + l + "[" + u.getClass().getName() + "]");
			}
		}
		
		List<Async> asyncs = asyncUpdatables;
		size = asyncs.size();
		Executor exe = asyncExecutor;
		for (int i = 0; i < size; i++) {
			Async r = asyncs.get(i);
			if (r.isDone()) {
				exe.execute(r);
			}
		}
		return true;
	}

	@Override
	public void add(Updatable u) {
		updatables.add(u);
	}

	@Override
	public void addAsync(final Updatable u) {
		asyncUpdatables.add(new Async(u));
	}

}
